﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace _2FA
{
    public class TOTP
    {
        // 生成密钥
        public static string GenerateSecret()
        {
            var rng = new RNGCryptoServiceProvider();
            var secret = new byte[20];
            rng.GetBytes(secret);
            return Base32Encoding.ToString(secret);
        }

        // 将字符串转换为字节数组
        private static byte[] StringToByteArray(string input)
        {
            return System.Text.Encoding.UTF8.GetBytes(input);
        }

        // 生成 TOTP 动态验证码
        public static string GenerateCode(string secret)
        {
            var key = Base32Encoding.ToBytes(secret);

            // 时间间隔为 30 秒
            var timeStep = 30;
            var unixTime = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;

            // 将当前时间转换为时间间隔内的时间段
            var timeSlice = (unixTime / timeStep);

            using (var hmac = new HMACSHA1(key))
            {
                // 计算哈希值
                var challenge = BitConverter.GetBytes(timeSlice).Reverse().ToArray();
                var hash = hmac.ComputeHash(challenge);

                // 摘要计数器
                var offset = hash[hash.Length - 1] & 0x0F;
                var truncatedHash = new byte[4];
                Array.Copy(hash, offset, truncatedHash, 0, 4);
                truncatedHash[0] &= 0x7F;

                // 将哈希值转换为整数并取模，得到 TOTP 动态验证码
                var code = BitConverter.ToInt32(truncatedHash.Reverse().ToArray(), 0) % 1000000;
                return code.ToString("D6");
            }
        }

        public static TimeSpan GetRemainingTime()
        {
            // 时间间隔为 30 秒
            var timeStep = 30;
            var unixTime = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;

            // 将当前时间转换为时间间隔内的时间段
            var timeSlice = (unixTime / timeStep);

            // 计算当前时间段的起始时间和结束时间
            var endTime = (timeSlice + 1) * timeStep;

            // 计算距离下一个时间段的剩余时间
            var remainingTime = TimeSpan.FromSeconds(endTime - unixTime);
            return remainingTime;
        }
    }
}
